﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;

[assembly: WebResource("EnhancedWebControls.Resources.NavBarDefaultStyleSheet.css", "text/css")]
namespace EnhancedWebControls
{
    /// <summary>
    /// Handles the NavBar Clicked Event
    /// </summary>
    /// <param name="clickArgs"></param>
    public delegate void NavBarClickedEventHandler(NavBarClickArgs clickArgs);
    /// <summary>
    /// Object Driven Navigation Menu
    /// </summary>
    [ToolboxData("<{0}:NavBar runat=server></{0}:NavBar>"), ParseChildren(true, "DataSource")]
    public class NavBar : CompositeControl
    {
        /// <summary>
        /// Constant Values used in determining the positioning of the Drop Down Menu
        /// </summary>
        public enum NavBarMenuPosition
        {
            BelowMenuItem = 1,
            RightOfMenuItem = 2
        }
        #region Events
        /// <summary>
        /// Event thrown when a NavBar item that is not a Hyperlink is clicked
        /// </summary>
        public event NavBarClickedEventHandler NavBarClicked;
        private void NavBarLinks_OnCommand(object sender, CommandEventArgs e)
        {
            if (e.CommandName == "Navigate")
            {
                NavBarClickArgs args = new NavBarClickArgs();
                string[] commandArgs = e.CommandArgument.ToString().Split(',');
                args.NavigateUrl = commandArgs[0];
                args.ClickedItemParentID = Convert.ToInt32(commandArgs[1]);
                args.ClickedItemMenuID = Convert.ToInt32(commandArgs[2]);
                OnNavBarClicked(args);
            }
        }
        protected virtual void OnNavBarClicked(NavBarClickArgs clickArgs)
        {
            if (NavBarClicked != null)
                NavBarClicked(clickArgs);
        }
        #endregion

        #region Private Storage
        private int _selectedParentId;
        #endregion

        #region Public Viewstate Properties
        /// <summary>
        /// Collection of NavBarMenuItems to be displayed in the navbar
        /// </summary>
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public List<NavBarMenuItem> DataSource
        {
            get
            {
                if (DesignMode)
                    return GetDesignTimeDataSource();
                else
                {
                    return (List<NavBarMenuItem>)ViewState["NavBarMenuItems"];
                }
            }
            set
            {
                ViewState["NavBarMenuItems"] = value;
            }
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets or Sets the IFrame Src to use for supporting Legacy browsers
        /// An Iframe will be used when the browser is IE6 or lower, the src of the Iframe will be null, 
        /// but if the Iframe appears on an SSL page you must provide a blank html file for the src value to avoid SSL errors.
        /// </summary>
        public string LegacyFrameSRC { get; set; }
        /// <summary>
        /// Gets or Sets whether the Navbar is rendered as Hyperlinks or as linkbuttons. 
        /// LinkButtons will cause a postback event containing navigation information and will not automatically navigate
        /// </summary>
        public bool RenderAsLinks { get; set; }
        /// <summary>
        /// Gets or Sets the location of the drop down menus relative to their parent button
        /// </summary>
        public NavBarMenuPosition DropDownMenuPosition { get; set; }
        /// <summary>
        /// Gets or Sets the Horizontal Offset of the drop down menus relative to their parent button
        /// </summary>
        public Unit DropDownMenuXOffset { get; set; }
        /// <summary>
        /// Gets or Sets the Vertical Offset of the drop down menus relative to their parent button
        /// </summary>
        public Unit DropDownMenuYOffset { get; set; }
        #endregion

        #region Override Methods
        public NavBar()
            : base()
        {
            DataSource = new List<NavBarMenuItem>();
        }
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (!DesignMode && ((System.Web.UI.HtmlControls.HtmlHead)Page.Header).FindControl("NavBarDefaultStyleSheet") == null)
            {
                string cssLink = "<link href=\"" + Page.ClientScript.GetWebResourceUrl(this.GetType(), "EnhancedWebControls.Resources.NavBarDefaultStyleSheet.css") + "\" type=\"text/css\" rel=\"stylesheet\" />";
                LiteralControl lc = new LiteralControl(cssLink);
                lc.ID = "NavBarDefaultStyleSheet";
                ((System.Web.UI.HtmlControls.HtmlHead)Page.Header).Controls.Add(lc);
            }
            DataBind();
        }
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            DataBind();
        }
        public override void DataBind()
        {
            base.OnDataBinding(EventArgs.Empty);
            Controls.Clear();

            if (HasChildViewState)
                ClearChildViewState();
            ChildControlsCreated = true;
            if (!IsTrackingViewState)
                TrackViewState();
            if (DataSource.Count() > 0)
            {
                foreach (NavBarMenuItem m in DataSource)
                {
                    if (!m.IsParent)
                        AddNavbarLinkAsControl(m);
                    else if (GetChildrenItems(m.ParentID).Count() == 0)
                        AddNavbarLinkAsControl(m); //parent item with no children is level 0 button
                }
            }
        }
        protected override void OnPreRender(EventArgs e)
        {
            EnsureChildControls();
            base.OnPreRender(e);
            ApplyClientScriptsToPage();
        }
        protected override void Render(HtmlTextWriter writer)
        {
            EnsureChildControls();
            RenderDesignTimeStyles(writer);
            RenderIframeForIE6(writer);
            RenderOuterControlBegin(writer);
            if (DataSource.Count() > 0)
            {
                IEnumerable<NavBarMenuItem> parents = GetParentItems();
                _selectedParentId = GetSelectedParentId();
                foreach (NavBarMenuItem p in parents)
                {
                    if (!p.HideInMenu)
                    {
                        IEnumerable<NavBarMenuItem> children = GetChildrenItems(p.MenuID);
                        if (children.Count() == 0)
                            RenderParentWebControl(p, writer); //render nav button
                        else
                        {
                            //render menu drop down
                            RenderMenuDropDownBegin(p, writer);
                            int index = 0;
                            int lastIndex = children.Count()-1;
                            foreach (NavBarMenuItem c in children)
                            {
                                if (!c.HideInMenu)
                                {
                                    RenderChildMenuWebControl(c, writer); //render menu link
                                    if (index < lastIndex)
                                        writer.WriteLine();
                                }
                                index++;
                            }
                            RenderMenuDropDownEnd(writer);
                        }
                    }
                }
            }
            RenderOuterControlEnd(writer);
        }
        #endregion

        #region Internal Render Methods
        private void RenderDesignTimeStyles(HtmlTextWriter writer)
        {
            if (DesignMode)
            {
                using (StreamReader reader = new StreamReader(this.GetType().Assembly.GetManifestResourceStream(this.GetType(), "Resources.NavBarDefaultStyleSheet.css")))
                {
                    string styleTag = "<style type=\"text/css\">" + reader.ReadToEnd() + "</style>";
                    reader.Close();
                    LiteralControl lc = new LiteralControl(styleTag);
                    lc.RenderControl(writer);
                }
            }
        }
        private void RenderIframeForIE6(HtmlTextWriter writer)
        {
            if (DataSource.Count() > 0)
            {
                if (!DesignMode)
                {
                    if (WebControlUtilities.IsBrowserIE6orLower(Page.Request.Browser))
                    {
                        writer.AddAttribute("id", ClientID + "_iframe");
                        writer.AddAttribute("src", LegacyFrameSRC);
                        writer.AddStyleAttribute("position", "absolute");
                        writer.AddStyleAttribute("width", "0px");
                        writer.AddStyleAttribute("height", "0px");
                        writer.AddStyleAttribute("visibility", "hidden");
                        writer.AddStyleAttribute("filter", "mask()");
                        writer.RenderBeginTag(HtmlTextWriterTag.Iframe);
                        writer.RenderEndTag();
                    }
                }
            }
        }
        private void AddNavbarLinkAsControl(NavBarMenuItem m)
        {
            if (m.OpenInNewWindow || RenderAsLinks) //Add Hyperlink
            {
                HyperLink hl = new HyperLink
                {
                    ID = m.MenuTextID,
                    ToolTip = m.MenuDescription,
                    Text = m.MenuText,
                    NavigateUrl = m.NavigateUrl
                };
                if (m.OpenInNewWindow)
                    hl.Target = "_blank";
                Controls.Add(hl);
            }
            else //Add Link Button
            {
                LinkButton lb = new LinkButton
                {
                    ID = m.MenuTextID,
                    ToolTip = m.MenuDescription,
                    Text = m.MenuText,
                    CommandName = "Navigate",
                    CommandArgument = BuildMenuLinkCommandArgument(m)
                };
                lb.Command += new CommandEventHandler(NavBarLinks_OnCommand);
                Controls.Add(lb);
            }
        }
        private void ApplyClientScriptsToPage()
        {
            if (DataSource.Count() > 0)
            {
                if (!DesignMode)
                {
                    if (Page.Request.Browser.IsBrowserIE6orLower())
                    {
                        string MoveIframeScriptKey = "MoveNavbarIframeScript";
                        if (Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), MoveIframeScriptKey))
                        {
                            StringBuilder script = new StringBuilder();
                            script.AppendLine("function moveIframe(divID,frameID){");
                            script.AppendLine("var elem = document.getElementById(divID);");
                            script.AppendLine("var frame = document.getElementById(frameID);");
                            script.AppendLine("frame.style.visibility='visible';");
                            script.AppendLine("frame.style.left = elem.offsetLeft + 'px';");
                            script.AppendLine("frame.style.top = elem.offsetTop + 'px';");
                            script.AppendLine("frame.style.height = elem.offsetHeight + 'px';");
                            script.AppendLine("frame.style.width = elem.offsetWidth + 'px';");
                            script.AppendLine("frame.style.zIndex = elem.style.zIndex - 1;");
                            script.AppendLine("}");
                            ScriptManager.RegisterClientScriptBlock(Page, this.GetType(), MoveIframeScriptKey, script.ToString(), true);
                        }
                    }
                }
            }
        }
        private string BuildMenuLinkCommandArgument(NavBarMenuItem m)
        {
            if (m.IsParent)
                return m.NavigateUrl + "," + m.MenuID.ToString() + "," + m.MenuID.ToString();
            else
                return m.NavigateUrl + "," + m.ParentID.ToString() + "," + m.MenuID.ToString();
        }
        protected virtual void RenderOuterControlBegin(HtmlTextWriter writer)
        {
            writer.AddAttribute("id", ClientID);
            if (string.IsNullOrEmpty(CssClass))
                writer.AddAttribute("class", "EWC__NavBar");
            else
                writer.AddAttribute("class", CssClass);
            foreach (string k in Style.Keys)
            {
                writer.AddStyleAttribute(k, Style[k]);
            }
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
        }
        protected virtual void RenderOuterControlEnd(HtmlTextWriter writer)
        {
            writer.RenderEndTag();
        }
        protected virtual void RenderChildMenuWebControl(NavBarMenuItem m, HtmlTextWriter writer)
        {
            WebControl currentItem = (WebControl)FindControl(m.MenuTextID);
            if ( currentItem != null)
            {
                currentItem.CssClass = "EWC__ChildMenuItem";
                currentItem.RenderControl(writer);
            }
        }
        protected virtual void RenderParentWebControl(NavBarMenuItem m, HtmlTextWriter writer)
        {
            WebControl currentItem = (WebControl)FindControl(m.MenuTextID);
            if (currentItem != null)
            {
                if (m.MenuID == _selectedParentId)
                    currentItem.CssClass = "EWC__NavButtonSelected";
                else
                {
                    currentItem.CssClass = "EWC__NavButton";
                    currentItem.Attributes.Add("onmousedown", "this.className='EWC__NavButtonPress';");
                    currentItem.Attributes.Add("onmouseover", "this.className='EWC__NavButtonHover';");
                    currentItem.Attributes.Add("onmouseout", "this.className='" + currentItem.CssClass + "';");
                }
                currentItem.RenderControl(writer);
                writer.WriteLine();
            }
        }
        private string GetDropDownMouseOverScript(string uniqueID)
        {
            StringBuilder rval = new StringBuilder();
            rval.Append("document.getElementById('" + uniqueID + "_button" + "').className='EWC__ParentButtonHover'; ");
            rval.Append("document.getElementById('" + uniqueID + "_dropdown" + "').style.display=''; ");
            if (!DesignMode && Page.Request.Browser.IsBrowserIE6orLower())
                rval.Append("moveIframe('" + uniqueID + "_dropdown" + "','" + ClientID + "_iframe');");
            return rval.ToString();
        }
        private string GetDropDownMouseOutScript(string uniqueID, string buttonCssClass)
        {
            StringBuilder rval = new StringBuilder();
            rval.Append("document.getElementById('" + uniqueID + "_button" + "').className='" + buttonCssClass + "'; ");
            rval.Append("document.getElementById('" + uniqueID + "_dropdown" + "').style.display='none'; ");
            if (!DesignMode && Page.Request.Browser.IsBrowserIE6orLower())
                rval.Append("document.getElementById('" + ClientID + "_iframe" + "').style.display='none'; ");
            return rval.ToString();
        }
        protected virtual void RenderMenuDropDownBegin(NavBarMenuItem m, HtmlTextWriter writer)
        {
            String buttonCssClass;
            if (m.MenuID == _selectedParentId)
                buttonCssClass = "EWC__ParentButtonSelected";
            else
                buttonCssClass = "EWC__ParentButton";

            writer.AddAttribute("id", ClientID + "_" + m.MenuTextID);
            writer.AddStyleAttribute("float", "left");
            writer.AddStyleAttribute("clear", "none");
            writer.AddStyleAttribute("overflow", "hidden");
            writer.AddAttribute("onmouseover", GetDropDownMouseOverScript(ClientID + "_" + m.MenuTextID));
            writer.AddAttribute("onclick", GetDropDownMouseOverScript(ClientID + "_" + m.MenuTextID));
            writer.AddAttribute("onmouseout", GetDropDownMouseOutScript(ClientID + "_" + m.MenuTextID, buttonCssClass));
            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            writer.WriteBeginTag("div");
            writer.WriteAttribute("id", ClientID + "_" + m.MenuTextID + "_button");
            writer.WriteAttribute("class", buttonCssClass);
            writer.WriteAttribute("title", buttonCssClass);
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.WriteEncodedText(m.MenuText);
            writer.WriteEndTag("div");
            writer.WriteLine();

            writer.AddStyleAttribute("float", "left");
            if (DropDownMenuPosition == NavBarMenuPosition.RightOfMenuItem)
                writer.AddStyleAttribute("clear", "none");
            else
                writer.AddStyleAttribute("clear", "both");

            writer.AddStyleAttribute("font-size", "0px");
            writer.AddStyleAttribute("height", "0px");
            writer.AddStyleAttribute("margin", "0px");
            writer.AddStyleAttribute("padding", "0px");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            writer.AddAttribute("id", ClientID + "_" + m.MenuTextID + "_dropdown");
            writer.AddStyleAttribute("display", "none");
            writer.AddStyleAttribute("position", "absolute");
            writer.AddStyleAttribute("z-index", "100");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            writer.AddStyleAttribute("position", "relative");
            writer.AddStyleAttribute("overflow", "hidden");
            if (!DropDownMenuXOffset.IsEmpty)
                writer.AddStyleAttribute("left", DropDownMenuXOffset.ToString());
            if (!DropDownMenuYOffset.IsEmpty)
                writer.AddStyleAttribute("top", DropDownMenuYOffset.ToString());

            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            writer.AddAttribute("class", "EWC__ChildMenu");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
        }
        protected virtual void RenderMenuDropDownEnd(HtmlTextWriter writer)
        {
            //menu
            writer.RenderEndTag();

            //relative container
            writer.RenderEndTag();

            //absolute container
            writer.RenderEndTag();

            //float container
            writer.RenderEndTag();

            //drop down container
            writer.RenderEndTag();
            writer.WriteLine();
        }
        #endregion

        #region Helper Methods
        private IEnumerable<NavBarMenuItem> GetParentItems()
        {
            return from x in DataSource where x.IsParent == true select x;
        }
        private IEnumerable<NavBarMenuItem> GetChildrenItems(int parentID)
        {
            return from x in DataSource where x.IsParent == false && x.ParentID == parentID && x.HideInMenu == false select x;
        }
        protected int GetSelectedParentId()
        {
            NavBarMenuItem selectedItem = DataSource.FirstOrDefault(m => string.Compare(Page.AppRelativeVirtualPath, m.NavigateUrl, true) == 0);
            if (selectedItem != null && !DesignMode)
            {
                if (selectedItem.IsParent)
                    return selectedItem.MenuID;
                else
                    return selectedItem.ParentID;
            }
            else
                return -1;
        }
        protected List<NavBarMenuItem> GetDesignTimeDataSource()
        {
            List<NavBarMenuItem> rval = new List<NavBarMenuItem>();
            rval.Add(new NavBarMenuItem { MenuID = 1, ParentID = 0, MenuText = "MenuItem 1", MenuDescription = "MenuItemDescription", NavigateUrl = "#", HideInMenu = false, OpenInNewWindow = false });
            rval.Add(new NavBarMenuItem { MenuID = 2, ParentID = 0, MenuText = "MenuItem 2", MenuDescription = "MenuItemDescription", NavigateUrl = "#", HideInMenu = false, OpenInNewWindow = false });
            rval.Add(new NavBarMenuItem { MenuID = 3, ParentID = 0, MenuText = "MenuItem 3", MenuDescription = "MenuItemDescription", NavigateUrl = "#", HideInMenu = false, OpenInNewWindow = false });
            rval.Add(new NavBarMenuItem { MenuID = 4, ParentID = 3, MenuText = "MenuItem 3", MenuDescription = "MenuItemDescription", NavigateUrl = "#", HideInMenu = false, OpenInNewWindow = false });
            rval.Add(new NavBarMenuItem { MenuID = 5, ParentID = 3, MenuText = "MenuItem 3", MenuDescription = "MenuItemDescription", NavigateUrl = "#", HideInMenu = false, OpenInNewWindow = false });
            return rval;
        }
        #endregion
    }
}